home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / DirectInput / DIConfig / flexwnd.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  13.7 KB  |  622 lines

  1. //-----------------------------------------------------------------------------
  2. // File: flexwnd.cpp
  3. //
  4. // Desc: CFlexWnd is a generic class that encapsulates the functionalities
  5. //       of a window.  All other window classes are derived from CFlexWnd.
  6. //
  7. //       Child classes can have different behavior by overriding the
  8. //       overridable message handlers (OnXXX members).
  9. //
  10. // Copyright (C) Microsoft Corporation. All Rights Reserved.
  11. //-----------------------------------------------------------------------------
  12.  
  13. #include "common.hpp"
  14. #include "typeinfo.h"
  15.  
  16. BOOL CFlexWnd::sm_bWndClassRegistered = FALSE;
  17. WNDCLASSEX CFlexWnd::sm_WndClass;
  18. LPCTSTR CFlexWnd::sm_tszWndClassName = _T("Microsoft.CFlexWnd.WndClassName");
  19. HINSTANCE CFlexWnd::sm_hInstance = NULL;
  20. CFlexToolTip CFlexWnd::s_ToolTip;  // Shared tooltip window object
  21. DWORD CFlexWnd::s_dwLastMouseMove;  // Last GetTickCount() that we have a WM_MOUSEMOVE
  22. HWND CFlexWnd::s_hWndLastMouseMove;  // Last window handle of WM_MOUSEMOVE
  23. LPARAM CFlexWnd::s_PointLastMouseMove;  // Last point of WM_MOUSEMOVE
  24. HWND CFlexWnd::s_CurrPageHwnd;  // For unhighlighting callouts when a click is made outside of a callout
  25.  
  26.  
  27. int NewID()
  28. {
  29.     static int i = 0;
  30.     return ++i;
  31. }
  32.  
  33. CFlexWnd::CFlexWnd() : m_nID(NewID()),
  34.     m_hWnd(m_privhWnd), m_privhWnd(NULL), m_hRenderInto(NULL),
  35.     m_bIsDialog(FALSE),    m_bRender(FALSE),
  36.     m_bReadOnly(FALSE)
  37. {
  38. }
  39.  
  40. CFlexWnd::~CFlexWnd()
  41. {
  42.     Destroy();
  43. }
  44.  
  45. void CFlexWnd::Destroy()
  46. {
  47.     if (m_hWnd != NULL)
  48.         DestroyWindow(m_hWnd);
  49.  
  50.     assert(m_privhWnd == NULL);
  51. }
  52.  
  53. BOOL CFlexWnd::IsDialog()
  54. {
  55.     return HasWnd() && m_bIsDialog;
  56. }
  57.  
  58. void CFlexWnd::OnRender(BOOL bInternalCall)
  59. {
  60.     // if parent is flexwnd and both are in render mode, pass to parent
  61.     if (!m_hWnd)
  62.         return;
  63.     HWND hParent = GetParent(m_hWnd);
  64.     if (!hParent)
  65.         return;
  66.     CFlexWnd *pParent = GetFlexWnd(hParent);
  67.     if (!pParent)
  68.         return;
  69.     if (pParent->InRenderMode() && InRenderMode())
  70.         pParent->OnRender(TRUE);
  71. }
  72.  
  73. BOOL CFlexWnd::OnEraseBkgnd(HDC hDC)
  74. {
  75.     if (InRenderMode())
  76.         return TRUE;
  77.  
  78. /*    if (IsDialog())
  79.         return FALSE;*/
  80.  
  81.     return TRUE;
  82. }
  83.  
  84. struct GETFLEXWNDSTRUCT {
  85.     int cbSize;
  86.     BOOL bFlexWnd;
  87.     CFlexWnd *pFlexWnd;
  88. };
  89.  
  90. // This function takes a HWND and returns a pointer to CFlexWnd if the HWND is a window
  91. // created by the UI.
  92. CFlexWnd *CFlexWnd::GetFlexWnd(HWND hWnd)
  93. {
  94.     if (hWnd == NULL)
  95.         return NULL;
  96.  
  97.     GETFLEXWNDSTRUCT gfws;
  98.     gfws.cbSize = sizeof(gfws);
  99.     gfws.bFlexWnd = FALSE;
  100.     gfws.pFlexWnd = NULL;
  101.     SendMessage(hWnd, WM_GETFLEXWND, 0, (LPARAM)(LPVOID)(FAR GETFLEXWNDSTRUCT *)&gfws);
  102.  
  103.     if (gfws.bFlexWnd)
  104.         return gfws.pFlexWnd;
  105.     else
  106.         return NULL;
  107. }
  108.  
  109. // Basic window proc. It simply forward interesting messages to the appropriate handlers (OnXXX).
  110. // If child class defines this function, it should pass unhandled messages to CFlexWnd.
  111. LRESULT CFlexWnd::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  112. {
  113.     switch (msg)
  114.     {
  115.         case WM_GETFLEXWND:
  116.         {
  117.             if ((LPVOID)lParam == NULL)
  118.                 break;
  119.  
  120.             GETFLEXWNDSTRUCT &gfws = *((FAR GETFLEXWNDSTRUCT *)(LPVOID)lParam);
  121.  
  122.             switch (gfws.cbSize)
  123.             {
  124.                 case sizeof(GETFLEXWNDSTRUCT):
  125.                     gfws.bFlexWnd = TRUE;
  126.                     gfws.pFlexWnd = this;
  127.                     return 0;
  128.  
  129.                 default:
  130.                     assert(0);
  131.                     break;
  132.             }
  133.             break;
  134.         }
  135.  
  136.         case WM_CREATE:
  137.         {
  138.             LPCREATESTRUCT lpCreateStruct = (LPCREATESTRUCT)lParam;
  139.             
  140.             LRESULT lr = OnCreate(lpCreateStruct);
  141.             
  142.             if (lr != -1)
  143.                 OnInit();
  144.  
  145.             return lr;
  146.         }
  147.  
  148.         case WM_INITDIALOG:
  149.         {
  150.             BOOL b = OnInitDialog();
  151.             OnInit();
  152.             return b;
  153.         }
  154.  
  155.         case WM_TIMER:
  156.             OnTimer((UINT)wParam);
  157.             return 0;
  158.  
  159.         case WM_ERASEBKGND:
  160.             return OnEraseBkgnd((HDC)wParam);
  161.  
  162.         case WM_PAINT:
  163.         {
  164.             // Check the update rectangle.  If we don't have it, exit immediately.
  165.             if (typeid(*this) == typeid(CDeviceView) && !GetUpdateRect(m_hWnd, NULL, FALSE))
  166.                 return 0;
  167.             PAINTSTRUCT ps;
  168.             HDC    hDC = BeginPaint(hWnd, &ps);
  169.             if (InRenderMode())
  170.                 OnRender(TRUE);
  171.             else
  172.                 DoOnPaint(hDC);
  173.             EndPaint(hWnd, &ps);
  174.             return 0;
  175.         }
  176.  
  177.         case WM_COMMAND:
  178.         {
  179.             WORD wNotifyCode = HIWORD(wParam);
  180.             WORD wID = LOWORD(wParam);
  181.             HWND hWnd = (HWND)lParam;
  182.             return OnCommand(wNotifyCode, wID, hWnd);
  183.         }
  184.  
  185.         case WM_NOTIFY:    
  186.             return OnNotify(wParam, lParam);
  187.  
  188.         case WM_MOUSEMOVE:
  189.         case WM_LBUTTONDOWN:
  190.         case WM_RBUTTONDOWN:
  191.         case WM_LBUTTONDBLCLK:
  192.         case WM_MOUSEWHEEL:
  193.         {
  194.             POINT point = {int(LOWORD(lParam)), int(HIWORD(lParam))};
  195.             switch (msg)
  196.             {
  197.                 case WM_MOUSEMOVE: OnMouseOver(point, wParam); break;
  198.                 case WM_LBUTTONDOWN: OnClick(point, wParam, TRUE); break;
  199.                 case WM_RBUTTONDOWN: OnClick(point, wParam, FALSE); break;
  200.                 case WM_LBUTTONDBLCLK: OnDoubleClick(point, wParam, TRUE); break;
  201.                 case WM_MOUSEWHEEL:
  202.                 {
  203.                     // Send wheel msg to the window beneath the cursor
  204.                     HWND hWnd = WindowFromPoint(point);
  205.                     CFlexWnd *pWnd = NULL;
  206.                     if (hWnd)
  207.                     {
  208.                         pWnd = GetFlexWnd(hWnd);
  209.                         if (pWnd)
  210.                             pWnd->OnWheel(point, wParam);
  211.                         else
  212.                             return DefWindowProc(hWnd, msg, wParam, lParam);
  213.                     }
  214.                     break;
  215.                 }
  216.             }
  217.             return 0;
  218.         }
  219.  
  220.         case WM_DESTROY:
  221.             OnDestroy();
  222.             m_privhWnd = NULL;
  223.             return 0;
  224.     }
  225.  
  226.     if (!m_bIsDialog)
  227.         return DefWindowProc(hWnd, msg, wParam, lParam);
  228.     else
  229.         return 0;
  230. }
  231. static HMENU windex = 0;
  232.  
  233. BOOL CFlexWnd::EndDialog(int n)
  234. {
  235.     if (!m_bIsDialog || m_hWnd == NULL)
  236.     {
  237.         assert(0);
  238.         return FALSE;
  239.     }
  240.  
  241.     return ::EndDialog(m_hWnd, n);
  242. }
  243.  
  244. int CFlexWnd::DoModal(HWND hParent, int nTemplate, HINSTANCE hInst)
  245. {
  246.     return DoModal(hParent, MAKEINTRESOURCE(nTemplate), hInst);
  247. }
  248.  
  249. HWND CFlexWnd::DoModeless(HWND hParent, int nTemplate, HINSTANCE hInst)
  250. {
  251.     return DoModeless(hParent, MAKEINTRESOURCE(nTemplate), hInst);
  252. }
  253.  
  254. int CFlexWnd::DoModal(HWND hParent, LPCTSTR lpTemplate, HINSTANCE hInst)
  255. {
  256.     if (m_hWnd != NULL)
  257.     {
  258.         assert(0);
  259.         return -1;
  260.     }
  261.  
  262.     if (hInst == NULL)
  263.         hInst = CFlexWnd::sm_hInstance;
  264.  
  265.     return (int)DialogBoxParam(hInst, lpTemplate, hParent,
  266.         (DLGPROC)__BaseFlexWndDialogProc, (LPARAM)(void *)this);
  267. }
  268.  
  269. HWND CFlexWnd::DoModeless(HWND hParent, LPCTSTR lpTemplate, HINSTANCE hInst)
  270. {
  271.     if (m_hWnd != NULL)
  272.     {
  273.         assert(0);
  274.         return NULL;
  275.     }
  276.  
  277.     if (hInst == NULL)
  278.         hInst = CFlexWnd::sm_hInstance;
  279.  
  280.     return CreateDialogParam(hInst, lpTemplate, hParent,
  281.         (DLGPROC)__BaseFlexWndDialogProc, (LPARAM)(void *)this);
  282. }
  283.  
  284. HWND CFlexWnd::Create(HWND hParent, const RECT &rect, BOOL bVisible)
  285. {
  286.     ++(*(LPBYTE*)&windex);
  287.     return Create(hParent, _T("(unnamed)"), 0,
  288.         WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_EX_NOPARENTNOTIFY | (bVisible ? WS_VISIBLE : 0),
  289.         rect, windex);
  290. }
  291.  
  292. HWND CFlexWnd::Create(HWND hParent, LPCTSTR tszName, DWORD dwExStyle, DWORD dwStyle, const RECT &rect, HMENU hMenu)
  293. {
  294.     HWND hWnd = NULL;
  295.  
  296.     if (m_hWnd != NULL)
  297.     {
  298.         assert(0);
  299.         return hWnd;
  300.     }
  301.  
  302.     if (hMenu == NULL && (dwStyle & WS_CHILD))
  303.     {
  304.         ++(*(LPBYTE*)&windex);
  305.         hMenu = windex;
  306.     }
  307.  
  308.     hWnd = CreateWindowEx(
  309.         dwExStyle,
  310.         CFlexWnd::sm_tszWndClassName,
  311.         tszName,
  312.         dwStyle,
  313.         rect.left, rect.top,
  314.         rect.right - rect.left, rect.bottom - rect.top,
  315.         hParent,
  316.         hMenu,
  317.         CFlexWnd::sm_hInstance,
  318.         (void *)this);
  319.  
  320.     assert(m_hWnd == hWnd);
  321.  
  322.     return hWnd;
  323. }
  324.  
  325. void CFlexWnd::SetHWND(HWND hWnd)
  326. {
  327.     assert(m_hWnd == NULL && hWnd != NULL);
  328.     m_privhWnd = hWnd;
  329.     assert(m_hWnd == m_privhWnd);
  330.  
  331.     InitFlexWnd();
  332. }
  333.  
  334. void CFlexWnd::InitFlexWnd()
  335. {
  336.     if (!HasWnd())
  337.         return;
  338.  
  339.     HWND hParent = GetParent(m_hWnd);
  340.     CFlexWnd *pParent = GetFlexWnd(hParent);
  341.     if (pParent && pParent->InRenderMode())
  342.         SetRenderMode();
  343. }
  344.  
  345. TCHAR sg_tszFlexWndPointerProp[] = _T("CFlexWnd *");
  346.  
  347. LRESULT CALLBACK __BaseFlexWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  348. {
  349.     CFlexWnd *pThis = (CFlexWnd *)GetProp(hWnd, sg_tszFlexWndPointerProp);
  350.  
  351.     if ((msg == WM_MOUSEMOVE || msg == WM_MOUSEWHEEL) && hWnd != CFlexWnd::s_ToolTip.m_hWnd)
  352.     {
  353.         // Filter out the message with same window handle and point.
  354.         // Windows sometimes seems to send us WM_MOUSEMOVE message even though the mouse is not moved.
  355.         if (CFlexWnd::s_hWndLastMouseMove != hWnd || CFlexWnd::s_PointLastMouseMove != lParam)
  356.         {
  357.             CFlexWnd::s_hWndLastMouseMove = hWnd;
  358.             CFlexWnd::s_PointLastMouseMove = lParam;
  359.             CFlexWnd::s_dwLastMouseMove = GetTickCount();  // Get timestamp
  360.             CFlexWnd::s_ToolTip.SetEnable(FALSE);
  361.             CFlexWnd::s_ToolTip.SetToolTipParent(NULL);
  362.         }
  363.     }
  364.  
  365.     switch (msg)
  366.     {
  367.         case WM_CREATE:
  368.         {
  369.             LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
  370.             if (lpcs == NULL)
  371.                 break;
  372.  
  373.             pThis = (CFlexWnd *)(void *)(lpcs->lpCreateParams);
  374.             assert(sizeof(HANDLE) == sizeof(CFlexWnd *));
  375.             SetProp(hWnd, sg_tszFlexWndPointerProp, (HANDLE)pThis);
  376.  
  377.             if (pThis != NULL)
  378.             {
  379.                 pThis->m_bIsDialog = FALSE;
  380.                 pThis->SetHWND(hWnd);
  381.             }
  382.             break;
  383.         }
  384.     }
  385.  
  386.     if (pThis != NULL)
  387.         return pThis->WndProc(hWnd, msg, wParam, lParam);
  388.     else
  389.         return DefWindowProc(hWnd, msg, wParam, lParam);
  390. }
  391.  
  392. LRESULT CALLBACK __BaseFlexWndDialogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  393. {
  394.     CFlexWnd *pThis = (CFlexWnd *)GetProp(hWnd, sg_tszFlexWndPointerProp);
  395.  
  396.     switch (msg)
  397.     {
  398.         case WM_INITDIALOG:
  399.             pThis = (CFlexWnd *)(void *)lParam;
  400.             assert(sizeof(HANDLE) == sizeof(CFlexWnd *));
  401.             SetProp(hWnd, sg_tszFlexWndPointerProp, (HANDLE)pThis);
  402.             if (pThis != NULL)
  403.             {
  404.                 pThis->m_bIsDialog = TRUE;
  405.                 pThis->SetHWND(hWnd);
  406.             }
  407.             break;
  408.     }
  409.     
  410.     if (pThis != NULL)
  411.         return (BOOL)pThis->WndProc(hWnd, msg, wParam, lParam);
  412.     else
  413.         return FALSE;
  414. }
  415.  
  416. void CFlexWnd::Invalidate()
  417. {
  418.     if (m_hWnd != NULL)
  419.         InvalidateRect(m_hWnd, NULL, TRUE);
  420. }
  421.  
  422. SIZE CFlexWnd::GetClientSize() const
  423. {
  424.     RECT rect = {0, 0, 0, 0};
  425.     if (m_hWnd != NULL)
  426.         ::GetClientRect(m_hWnd, &rect);
  427.     SIZE size = {
  428.         rect.right - rect.left,
  429.         rect.bottom - rect.top};
  430.     return size;
  431. }
  432.  
  433. void CFlexWnd::FillWndClass(HINSTANCE hInst)
  434. {
  435.     sm_WndClass.cbSize = sizeof(WNDCLASSEX);
  436.     sm_WndClass.style = CS_DBLCLKS;
  437.     sm_WndClass.lpfnWndProc = __BaseFlexWndProc;
  438.     sm_WndClass.cbClsExtra = 0;
  439.     sm_WndClass.cbWndExtra = sizeof(CFlexWnd *);
  440.     sm_WndClass.hInstance = sm_hInstance = hInst;
  441.     sm_WndClass.hIcon = NULL;
  442.     sm_WndClass.hCursor = NULL;
  443.     sm_WndClass.hbrBackground = NULL;
  444.     sm_WndClass.lpszMenuName = NULL;
  445.     sm_WndClass.lpszClassName = sm_tszWndClassName;
  446.     sm_WndClass.hIconSm = NULL;
  447. }
  448.  
  449. void CFlexWnd::RegisterWndClass(HINSTANCE hInst)
  450. {
  451.     if (hInst == NULL)
  452.     {
  453.         assert(0);
  454.         return;
  455.     }
  456.  
  457.     FillWndClass(hInst);
  458.     RegisterClassEx(&sm_WndClass);
  459.     sm_bWndClassRegistered = TRUE;
  460. }
  461.  
  462. void CFlexWnd::UnregisterWndClass(HINSTANCE hInst)
  463. {
  464.     if (hInst == NULL)
  465.         return;
  466.  
  467.     UnregisterClass(sm_tszWndClassName, hInst);
  468.     sm_bWndClassRegistered = FALSE;
  469. }
  470.  
  471. void CFlexWnd::GetClientRect(LPRECT lprect) const
  472. {
  473.     if (lprect == NULL || m_hWnd == NULL)
  474.         return;
  475.  
  476.     ::GetClientRect(m_hWnd, lprect);
  477. }
  478.  
  479. LPCTSTR CFlexWnd::GetDefaultClassName()
  480. {
  481.     return CFlexWnd::sm_tszWndClassName;
  482. }
  483.  
  484. void CFlexWnd::SetRenderMode(BOOL bRender)
  485. {
  486.     if (bRender == m_bRender)
  487.         return;
  488.  
  489.     m_bRender = bRender;
  490.     Invalidate();
  491. }
  492.  
  493. BOOL CFlexWnd::InRenderMode()
  494. {
  495.     return m_bRender;
  496. }
  497.  
  498. void EnumChildWindowsZDown(HWND hParent, WNDENUMPROC proc, LPARAM lParam)
  499. {
  500.     if (hParent == NULL || proc == NULL)
  501.         return;
  502.  
  503.     HWND hWnd = GetWindow(hParent, GW_CHILD);
  504.  
  505.     while (hWnd != NULL)
  506.     {
  507.         if (!proc(hWnd, lParam))
  508.             break;
  509.  
  510.         hWnd = GetWindow(hWnd, GW_HWNDNEXT);
  511.     }
  512. }
  513.  
  514. void EnumSiblingsAbove(HWND hParent, WNDENUMPROC proc, LPARAM lParam)
  515. {
  516.     if (hParent == NULL || proc == NULL)
  517.         return;
  518.  
  519.     HWND hWnd = hParent;
  520.  
  521.     while (1)
  522.     {
  523.         hWnd = GetWindow(hWnd, GW_HWNDPREV);
  524.  
  525.         if (hWnd == NULL)
  526.             break;
  527.  
  528.         if (!proc(hWnd, lParam))
  529.             break;
  530.     }
  531. }
  532.  
  533. static BOOL CALLBACK RenderIntoClipChild(HWND hWnd, LPARAM lParam)
  534. {
  535.     CFlexWnd *pThis = (CFlexWnd *)(LPVOID)lParam;
  536.     return pThis->RenderIntoClipChild(hWnd);
  537. }
  538.  
  539. static BOOL CALLBACK RenderIntoRenderChild(HWND hWnd, LPARAM lParam)
  540. {
  541.     CFlexWnd *pThis = (CFlexWnd *)(LPVOID)lParam;
  542.     // Check if this is the immediate child. Do nothing if it's not immediate.
  543.     HWND hParent = GetParent(hWnd);
  544.     if (hParent != pThis->m_hWnd)
  545.         return TRUE;
  546.     return pThis->RenderIntoRenderChild(hWnd);
  547. }
  548.  
  549. BOOL CFlexWnd::RenderIntoClipChild(HWND hChild)
  550. {
  551.     if (m_hRenderInto != NULL && HasWnd() && hChild && IsWindowVisible(hChild))
  552.     {
  553.         RECT rect;
  554.         GetWindowRect(hChild, &rect);
  555.         POINT ul = {rect.left, rect.top}, lr = {rect.right, rect.bottom};
  556.         ScreenToClient(m_hWnd, &ul);
  557.         ScreenToClient(m_hWnd, &lr);
  558.         ExcludeClipRect(m_hRenderInto, ul.x, ul.y, lr.x, lr.y);
  559.     }
  560.     return TRUE;
  561. }
  562.  
  563. BOOL CFlexWnd::RenderIntoRenderChild(HWND hChild)
  564. {
  565.     CFlexWnd *pChild = GetFlexWnd(hChild);
  566.     if (m_hRenderInto != NULL && HasWnd() && pChild != NULL && IsWindowVisible(hChild))
  567.     {
  568.         RECT rect;
  569.         GetWindowRect(hChild, &rect);
  570.         POINT ul = {rect.left, rect.top};
  571.         ScreenToClient(m_hWnd, &ul);
  572.         pChild->RenderInto(m_hRenderInto, ul.x, ul.y);
  573.     }
  574.     return TRUE;
  575. }
  576.  
  577. void CFlexWnd::RenderInto(HDC hDC, int x, int y)
  578. {
  579.     if (hDC == NULL)
  580.         return;
  581.         
  582.     int sdc = SaveDC(hDC);
  583.     {
  584.         OffsetViewportOrgEx(hDC, x, y, NULL);
  585.         SIZE size = GetClientSize();
  586.         IntersectClipRect(hDC, 0, 0, size.cx, size.cy);
  587.  
  588.         m_hRenderInto = hDC;
  589.  
  590.         int sdc2 = SaveDC(hDC);
  591.         {
  592.             EnumChildWindows/*ZDown*/(m_hWnd, ::RenderIntoClipChild, (LPARAM)(PVOID)this);
  593.             EnumSiblingsAbove(m_hWnd, ::RenderIntoClipChild, (LPARAM)(PVOID)this);
  594.             DoOnPaint(hDC);
  595.         }
  596.         if (sdc2)
  597.             RestoreDC(hDC, sdc2);
  598.  
  599.         EnumChildWindows/*ZDown*/(m_hWnd, ::RenderIntoRenderChild, (LPARAM)(PVOID)this);
  600.  
  601.         m_hRenderInto = NULL;
  602.     }
  603.  
  604.     if (sdc)
  605.         RestoreDC(hDC, sdc);
  606. }
  607.  
  608. void CFlexWnd::SetCapture()
  609. {
  610.     ::SetCapture(m_hWnd);
  611. }
  612.  
  613. void CFlexWnd::ReleaseCapture()
  614. {
  615.     ::ReleaseCapture();
  616. }
  617.  
  618. void CFlexWnd::DoOnPaint(HDC hDC)
  619. {
  620.     OnPaint(hDC);
  621. }
  622.